<template>
    <div class="app-container">
        <div class="filter-container">
            <el-input v-model="query.phone" class="filter-item w-200" placeholder="手机号码" size="medium" />
            <el-input v-model="query.name" class="filter-item w-200" placeholder="姓名" size="medium" />
            <el-select v-model="query.source_from" clearable class="filter-item w-200" placeholder="来源" size="medium">
                <el-option v-for="item in sources" :key="item.index" :value="item.index" :label="item.label"></el-option>
            </el-select>
            <el-select clearable class="w-200 filter-item" size="medium" v-model="query.role_id" filterable placeholder="用户角色">
                <el-option v-for="item in userRoles" :key="item.id" :value="item.id" :label="item.name"></el-option>
            </el-select>
            <el-button class="filter-item" icon="el-icon-search" @click="fetchList(resetPage = true)" size="medium">搜 索</el-button>
            <el-button class="filter-item" type="primary" icon="el-icon-plus" size="medium" style="float: right" @click="showAdd()" v-permission="'users.store'">新 增</el-button>
            <el-button class="filter-item" type="primary" icon="el-icon-plus" size="medium" style="float: right" @click="showImport" v-permission="'import-users.store'">导 入</el-button>
            <el-button class="filter-item" icon="el-icon-download" size="medium" style="float: right" @click="createTemplate">下载模版</el-button>
        </div>
        <el-table :data="users" v-loading="visible.listLoading" border stripe>
            <el-table-column label="#" align="center" width="100px">
                <template slot-scope="scope">
                    {{ query.size * (query.page - 1) + scope.$index + 1 }}
                </template>
            </el-table-column>
            <el-table-column label="id" prop="id" align="center"></el-table-column>
            <el-table-column label="姓名" prop="name" align="center"></el-table-column>
            <el-table-column label="手机号码" prop="phone" align="center"></el-table-column>
            <el-table-column label="状态" prop="status" align="center">
                <template slot-scope="scope">
                    <i class="el-icon-check" v-if="scope.row.status === 1" />
                    <i class="el-icon-close" v-else />
                </template>
            </el-table-column>
            <el-table-column label="角色名称" prop="role.name" align="center"></el-table-column>
            <el-table-column label="来源" prop="source_format" align="center"></el-table-column>
            <el-table-column label="早餐规则" prop="morning_rule" align="center">
                <template slot-scope="scope">
                    {{ scope.row.morning_rule === 1 ? '自动' : '手动' }}
                </template>
            </el-table-column>
            <el-table-column label="午餐规则" prop="lunch_rule" align="center">
                <template slot-scope="scope">
                    {{ scope.row.lunch_rule === 1 ? '自动' : '手动' }}
                </template>
            </el-table-column>
            <el-table-column label="晚餐规则" prop="dinner_rule" align="center">
                <template slot-scope="scope">
                    {{ scope.row.dinner_rule === 1 ? '自动' : '手动' }}
                </template>
            </el-table-column>
            <el-table-column label="创建时间" prop="created_at" align="center"></el-table-column>
            <el-table-column label="操作" align="center">
                <template slot-scope="scope">
                    <el-button class="filter-item" icon="el-icon-edit" type="primary" size="mini" v-permission="'users.update'" @click="showEdit(scope.row)">编 辑</el-button>
                    <el-button class="filter-item" :type="scope.row.status === 1 ? 'danger' : 'primary'"
                               :icon="scope.row.status === 1 ? 'el-icon-close' : 'el-icon-check'"
                               :loading="visible.statusLoading"
                               size="mini" v-permission="'users.destroy'" @click="showChange(scope.row)">{{ scope.row.status === 1 ? '禁 用' : '启 用' }}</el-button>
                </template>
            </el-table-column>
        </el-table>
        <div class="pagination-container">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="query.page"
                    :page-sizes="[10, 20, 30, 50]"
                    :page-size="query.size"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>
        <el-dialog :title="formTitle" :visible.sync="visible.form" width="700px" @close="$refs['form'].resetFields()">
            <el-form :model="form" ref="form" label-width="120px" :rules="rules">
                <el-form-item label="姓名" prop="name" verify required>
                    <el-input v-model="form.name" placeholder="请输入昵称" />
                </el-form-item>
                <el-form-item label="手机号码" prop="phone">
                    <el-input v-model="form.phone" placeholder="请输入手机号码" />
                </el-form-item>
                <el-form-item label="用户角色" prop="role_id" verify required>
                    <el-select class="form-select" v-model="form.role_id" placeholder="请选择用户角色">
                        <el-option v-for="item in userRoles" :key="item.id" :value="item.id" :label="item.name"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="学号/教工号" prop="user_no">
                    <el-input v-model="form.user_no" placeholder="请输入学号/教工号" />
                </el-form-item>
                <el-form-item label="性别" prop="sex">
                    <el-select class="form-select" v-model="form.sex" placeholder="请选择性别">
                        <el-option v-for="item in sexs" :key="item.index" :value="item.index" :label="item.label">
                        </el-option>
                    </el-select>
                </el-form-item>
            </el-form>
            <div slot="footer" class="dialog-footer">
                <el-button @click="visible.form = false, $refs['form'].resetFields()">取 消</el-button>
                <el-button type="primary"  @click="handleForm" :loading="visible.formLoading">确 定</el-button>
            </div>
        </el-dialog>
        <el-dialog title="导入用户数据" :visible.sync="dialogVisible" width="600px" @close="$refs['userForm'].resetFields()">
            <el-form :model="userForm" :rules="userRules" ref="userForm" label-width="150px">
                <el-form-item label="用户角色" prop="role">
                    <el-select class="form-select" v-model="userForm.role" style="width: 80%" placeholder="请选择用户角色">
                        <el-option v-for="item in userRoles" :key="item.id" :value="item.id" :label="item.name"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="Xlsx表格" prop="users">
                    <template>
                        <input type="file" @change="importFile(this)" style="display: none" id="imFile" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/>
                        <el-button class="filter-item" icon="el-icon-upload2" @click="uploadFile" size="medium">导 入</el-button>
                        <span>只能上传xlsx文件</span>
                    </template>
                </el-form-item>
                <el-table :data="userForm.users" border v-loading="failLoading" v-show="userForm.users.length > 0" style="width: 100%" height="200px">
                    <el-table-column label="#" align="center">
                        <template slot-scope="scope">
                            {{ scope.$index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="name" label="姓名" align="center"></el-table-column>
                    <el-table-column prop="phone" label="电话号码" align="center"></el-table-column>
                    <el-table-column v-if="isImport" label="是否导入成功" align="center">
                        <template slot-scope="scope">
                            <i class="el-icon-close" v-if="(failedPhones.indexOf(scope.row.phone) >= 0) || !scope.row.name || !scope.row.phone" />
                            <i class="el-icon-check" v-else />
                        </template>
                    </el-table-column>
                </el-table>
            </el-form>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogVisible = false, $refs['userForm'].resetFields()">取 消</el-button>
                <el-button type="primary" @click="submitUserForm" :loading="btnLoading">确 定</el-button>
            </div>
        </el-dialog>
    </div>
</template>

<script>
  import { fetchUsers, addUsers, editUsers, changeUsers } from '@/api/user'
  import { fetchData } from '@/api/data'
  import { importUsers } from '@/api/import'
  import { isPhoneAvailable } from '@/utils/validate'
  import XLSX from 'xlsx'
  export default {
    name: 'user_list',
    data() {
      const validPhone = (rule, value, callback) => {
        if (!value) {
          callback(new Error('请输入电话号码'))
        } else if (!isPhoneAvailable(value)) {
          callback(new Error('请输入正确的11位手机号码'))
        } else {
          callback()
        }
      }
      return {
        userForm: {
          role: '',
          users: []
        },
        btnLoading: false,
        dialogVisible: false,
        fullscreenLoading: false, // 加载中
        imFile: '', // 导入文件el
        query: {
          phone: null,
          source_from: null,
          name: null,
          role_id: null,
          size: 10,
          page: 1
        },
        visible: {
          formLoading: false,
          listLoading: false,
          detail: false,
          form: false,
          statusLoading: false
        },
        form: {
          name: '',
          phone: '',
          role_id: '',
          user_no: '',
          sex: ''
        },
        sexs: [
          {
            index: 1,
            label: '男'
          },
          {
            index: 2,
            label: '女'
          }
        ],
        users: [],
        userRoles: [],
        total: 0,
        editRow: '',
        formTitle: '',
        formType: '',
        school_id: '',
        sources: [
          {
            index: 'backend',
            label: '后台添加'
          },
          {
            index: 'school',
            label: '学校同步'
          },
          {
            index: 'import',
            label: '后台导入'
          }
        ],
        rules: {
          phone: [
            { required: true, trigger: ['blur', 'change'], validator: validPhone }
          ]
        },
        userRules: {
          role: [
            { required: true, message: '请选择用户角色', trigger: ['blur', 'change'] }
          ],
          users: [
            { required: true, message: '请选取表格以导入用户', trigger: ['blur', 'change'] }
          ]
        },
        failedPhones: [],
        isImport: false,
        failLoading: false
      }
    },
    methods: {
      // 下载案例模版
      createTemplate() {
        const filename = this.$moment().format('YYYY-MM-DD') + '-' + parseInt(10000 * Math.random())
        const wopts = { bookType: 'xlsx', bookSST: false, type: 'binary' }
        const list = [{ 'name': '张三', 'phone': '187xxxx1818' }, { 'name': '李四', 'phone': '152xxxx9918' }, { 'name': '王五', 'phone': '137xxxx4237' }]
        const tHeader = ['name', 'phone']
        const filterVal = ['name', 'phone']
        const data = this.formatJson(filterVal, list)
        data.unshift(tHeader)
        const wb = { SheetNames: [], Sheets: {}}
        const ws = this.sheet_from_array_of_arrays(data)
        ws['!cols'] = []
        for (let n = 0; n !== filterVal.length; ++n) {
          ws['!cols'].push({
            wpx: 100
          })
        }
        ws['!rows'] = [{ hpx: 20 }]
        wb.SheetNames.push(filename)
        wb.Sheets[filename] = ws
        const tmpDown = new Blob([this.s2ab(this.$xlsx.write(wb, wopts))], { type: 'application/octet-stream' })
        this.$fileSaver.saveAs(tmpDown, 'example.xlsx')
      },
      sheet_from_array_of_arrays(data) {
        const ws = {}
        const range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 }}
        for (let R = 0; R !== data.length; ++R) {
          for (let C = 0; C !== data[R].length; ++C) {
            if (range.s.r > R) range.s.r = R
            if (range.s.c > C) range.s.c = C
            if (range.e.r < R) range.e.r = R
            if (range.e.c < C) range.e.c = C
            const cell = {
              v: data[R][C],
              s: {
                alignment: { horizontal: 'center', vertical: 'center' }
              }
            }
            if (cell.v == null) continue
            const cell_ref = this.$xlsx.utils.encode_cell({ c: C, r: R })
            if (typeof cell.v === 'number') {
              cell.t = 'n'
            } else if (typeof cell.v === 'boolean') {
              cell.t = 'b'
            } else if (cell.v instanceof Date) {
              cell.t = 'n'
              cell.z = this.$xlsx.SSF._table[14]
              cell.v = this.datenum(cell.v)
            } else {
              cell.t = 's'
            }
            ws[cell_ref] = cell
          }
        }
        if (range.s.c < 10000000) ws['!ref'] = this.$xlsx.utils.encode_range(range)
        return ws
      },
      datenum(v, date1904) {
        if (date1904) v += 1462
        const epoch = Date.parse(v)
        return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
      },
      s2ab(s) {
        const buf = new ArrayBuffer(s.length)
        const view = new Uint8Array(buf)
        for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF
        return buf
      },
      formatJson(filterVal, jsonData) {
        return jsonData.map(v => filterVal.map(j => v[j]))
      },
      // 点击导入
      showImport() {
        this.dialogVisible = true
        this.failedPhones = []
        this.isImport = false
        this.failLoading = false
      },
      // 提交导入表单
      submitUserForm() {
        this.$refs.userForm.validate().then(valid => {
          if (valid) {
            this.failLoading = true
            this.btnLoading = true
            importUsers(this.userForm).then(res => {
              if (res.failed > 0) {
                // 有失败的数据
                this.isImport = true
                this.failedPhones = res.failedPhones
                this.$message.error('总共: ' + res.count + '条数据, 成功: ' + (res.count - res.failed) + '条, 失败: ' + res.failed + '条')
              } else {
                this.$message.success('成功导入: ' + res.count + '条用户记录')
                this.dialogVisible = false
              }
              this.fetchList()
              this.btnLoading = false
              this.failLoading = false
            }).catch(err => {
              this.$handleNotFound(err)
              this.btnLoading = false
              this.failLoading = false
            })
          }
        }).catch(() => {})
      },
      // 导入表格
      uploadFile() {
        this.imFile = document.getElementById('imFile')
        this.imFile.click()
      },
      importFile() {
        this.failedPhones = []
        this.isImport = false
        this.failLoading = false
        this.fullscreenLoading = true
        const obj = this.imFile
        if (!obj.files) {
          this.fullscreenLoading = false
          return
        }
        const f = obj.files[0]
        const reader = new FileReader()
        const $t = this
        reader.onload = (e) => {
          this.$refs.userForm.clearValidate('users')
          const data = e.target.result
          if ($t.rABS) {
            $t.wb = XLSX.read(btoa(this.fixdata(data)), {
              type: 'base64'
            })
          } else {
            $t.wb = XLSX.read(data, {
              type: 'binary'
            })
          }
          const json = XLSX.utils.sheet_to_json($t.wb.Sheets[$t.wb.SheetNames[0]])
          $t.dealFile(json)
        }
        if (this.rABS) {
          reader.readAsArrayBuffer(f)
        } else {
          reader.readAsBinaryString(f)
        }
      },
      dealFile(data) {
        this.imFile.value = ''
        this.fullscreenLoading = false
        if (data.length <= 0) {
          this.$message.info('表格数据为空')
        } else {
          this.userForm.users = data
        }
      },
      fixdata(data) {
        let o = ''
        let l = 0
        const w = 10240
        for (; l < data.byteLength / w; ++l) {
          o += String.fromCharCode.apply(
            null,
            new Uint8Array(data.slice(l * w, l * w + w))
          )
        }
        o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
        return o
      },
      handleSizeChange(size) {
        this.query.size = size
        this.fetchList()
      },
      handleCurrentChange(page) {
        this.query.page = page
        this.fetchList()
      },
      fetchList(resetPage = false) {
        if (resetPage === true) {
          this.query.page = 1
        }
        this.visible.listLoading = true
        fetchUsers(this.query).then(res => {
          this.users = res.data
          this.total = res.meta.total
          this.visible.listLoading = false
        })
      },
      // 重置表单验证
      resetForm(formName) {
        // 第一次新增数据时为空对象undefined
        if (this.$refs[formName] !== undefined) {
          this.$refs[formName].resetFields()
        }
      },
      showAdd() {
        this.resetForm('form')
        Object.assign(this.form, this.$options.data().form)
        this.visible.form = true
        this.formTitle = '新增用户'
        this.formType = 'add'
      },
      showEdit(row) {
        if (this.$refs['form'] !== undefined) {
          this.$refs['form'].clearValidate()
        }
        Object.existAssign(this.form, row)
        this.editRow = row
        this.visible.form = true
        this.formTitle = '编辑用户'
        this.formType = 'edit'
      },
      showChange(row) {
        this.msg = row.status === 1 ? '禁用' : '启用'
        this.$confirm('确定' + this.msg + '该用户?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.visible.statusLoading = true
          changeUsers(row.id).then(res => {
            this.visible.statusLoading = false
            row.status = res.status
            this.$message.success(this.msg + '成功')
          })
        }).catch(err => {
          this.visible.statusLoading = false
          this.$handleNotFound(err, '用户不存在')
        })
      },
      handleForm() {
        this.$refs.form.validate().then(valid => {
          if (valid) {
            this.visible.formLoading = true
            if (this.formType === 'add') {
              addUsers(this.form).then(res => {
                this.fetchList()
                this.visible.form = false
                this.$message.success('新增成功')
                this.visible.formLoading = false
              }).catch(err => {
                this.$handleNotFound(err)
                this.visible.formLoading = false
              })
            } else {
              editUsers(this.form, this.editRow.id).then(res => {
                this.fetchList()
                this.visible.form = false
                this.$message.success('编辑成功')
                this.visible.formLoading = false
              }).catch(err => {
                this.$handleNotFound(err, '用户不存在')
                this.visible.formLoading = false
              })
            }
          }
        }).catch(() => {})
      }
    },
    created() {
      this.fetchList()
      fetchData({ sources: ['userRoles'] }).then(res => {
        this.userRoles = res.userRoles
      })
    }
  }
</script>

<style scoped>
    .form-select{
        width: 100%;
    }
    .el-icon-check{
        color: green;
    }
    .el-icon-close{
        color: red;
    }
</style>
